import { PropertyData, MessageType, GetMemoryPackData, GetTypeOf } from "../../../../../Decorator/Decorator";
import { IMessage } from "../../../Share/Module/Message/IMessage";
import { MemoryPackReader } from "./MemoryPackReader";
import { MemoryPackWriter } from "./MemoryPackWriter";

export class MemoryPackSerializeHelper {

    static SerializeMember<T extends IMessage>(writer: MemoryPackWriter, propertyData: PropertyData, value: T | null) {
        const { order, name, type } = propertyData;
        switch (type) {
            case "number":
                writer.writeInt32(value[name]);
                break;
            case "bigint":
                writer.writeInt64(value[name]);
                break;
            case "string":
                writer.writeString(value[name]);
                break;
            case "enum":
                writer.writeInt32(value[name]);
                break;
            default:
                MemoryPackSerializeHelper.SerializeCore(writer, GetTypeOf(type as MessageType), value[name]);
                break;
        }
    }

    static DeserializeMember<T extends IMessage>(reader: MemoryPackReader, value: T | null, propertyData: PropertyData) {
        const { order, name, type } = propertyData;
        switch (type) {
            case "number":
                value[name] = reader.readInt32();
                break;
            case "bigint":
                value[name] = reader.readInt64();
                break;
            case "string":
                value[name] = reader.readString();
                break;
            case "enum":
                value[name] = reader.readInt32();
                break;
            default:
                value[name] = MemoryPackSerializeHelper.DeserializeCore(reader, GetTypeOf(type as MessageType));
                break;
        }
    }

    static SerializeCore<T extends IMessage>(writer: MemoryPackWriter, type: string, value: T | null) {
        if (value == null) {
            writer.writeNullObjectHeader();
            return;
        }
        const list = GetMemoryPackData(type);
        const memberCount = list.length + 1;
        writer.writeObjectHeader(memberCount);
        writer.writeBoolean(false);
        for (let i = 0; i < memberCount - 1; i++) {
            const propertyData = list[i];
            MemoryPackSerializeHelper.SerializeMember(writer, propertyData, value);
        }
    }

    static DeserializeCore<T extends IMessage>(reader: MemoryPackReader, type: string): T {
        const [ok, count] = reader.tryReadObjectHeader();
        if (!ok) {
            return null;
        }
        const list = GetMemoryPackData(type);
        const memberCount = list.length + 1;
        const value = Object.assign({}) as T;
        if (count == memberCount) {
            reader.readBoolean();
            for (let i = 0; i < memberCount - 1; i++) {
                const propertyData = list[i];
                MemoryPackSerializeHelper.DeserializeMember(reader, value, propertyData);
            }
        } else if (count > memberCount) {
            throw new Error("Current object's property count is larger than type schema, can't deserialize about versioning.");
        } else {
            if (count == 0) return value;
            reader.readBoolean(); if (count == 1) return value;
            for (let i = 0; i < count; i++) {
                const propertyData = list[i];
                MemoryPackSerializeHelper.DeserializeMember(reader, value, propertyData);
            }
        }
        return value;
    }

    static Serialize<T extends IMessage>(type: string, value: T | null) {
        const writer = MemoryPackWriter.getSharedInstance();
        MemoryPackSerializeHelper.SerializeCore(writer, type, value);
        return writer.toArray();
    }

    static Deserialize<T extends IMessage>(type: string, buffer: ArrayBuffer): T {
        return MemoryPackSerializeHelper.DeserializeCore(new MemoryPackReader(buffer), type);
    }

}